// 基准值, base on chrono
// 2023-1023


use crate::sync::map_hash::SyncHashMap;
use once_cell::sync::Lazy;
use std::str::FromStr;

use super::u3_native::SystemTime;
use super::a5_timezone::TimeZone;

use super::{b1_date::DateFT, b2_time::TimeFT, b3_datetime::DateTimeFT};


/// basic_ft value from chrono
#[derive(Clone, Copy)]
pub(crate) struct BasicFromChrono {
    pub timezone: TimeZone,
    pub micros_offset: i64,
    pub date: DateFT,
    /// in micros
    pub timestamp_date_0_oclock: i64,

}
impl BasicFromChrono {
    pub fn to_print(&self) -> String {
        let mut buf = "FastTime时间库(基于std::time标准库)Chrono基准数据(BasicFromChrono)明细:\n".to_string();
        buf.push_str(&format!("\t时区:\t{}\n", self.timezone));
        let hours_offset = self.timezone.hours_offset();
        buf.push_str(&format!("\t偏移量:\t{hours_offset} 小时;\t{} 秒\n", hours_offset * 3600));
        buf.push_str(&format!("\t启动:\t{}\n", self.date.to_print_cn()));
        buf.push_str(&format!("\t正0时时间戳:\t{} 微秒\n", self.timestamp_date_0_oclock));


        buf
    }
}
fn get_date() -> (i32, u8, u8) {
    use chrono::{Datelike, Local};
    let now = Local::now();
    (now.year(), now.month() as u8, now.day() as u8)
}



/// 操作系统时区信息 + 启动时间的基准时间戳
pub(crate) static BASIC_INFO_STARTUP: Lazy<BasicFromChrono> = Lazy::new(|| {
    // let (timezone, micros_offset) = datetime_wall::TIMEZONE_MICROS_OFFSET.clone();
    let now = chrono::Local::now();
    
    // timezone
    let offset = now.offset().local_minus_utc();
    let timezone = if offset % 3600 != 0 {
        TimeZone::UTC_00
    } else {
        TimeZone::from_i32(offset / 3600)
    };
    let hours_offset = timezone.hours_offset() as i64;
    let micros_offset = 3600_000_000_i64 * hours_offset;
    // basic_ft timestamp
    let (year, month, day) = get_date();
    let date_today = DateFT::from_ymd_unsafe(year, month, day);
    let str_today = format!(
        "{year}-{month:0>2}-{day:0>2}T00:00:00",
    );
    let navie_today = chrono::NaiveDateTime::from_str(&str_today).expect("Unreachable");
    let timestamp_micros = navie_today.and_utc().timestamp_micros();
    let timestamp_date_0_oclock = timestamp_micros - micros_offset;

    let basic_info = BasicFromChrono {
        timezone,
        micros_offset,
        date: date_today,
        timestamp_date_0_oclock,
    };
    let basic_info_str = basic_info.to_print();

    MAP_YMD_BASICINFO.insert(date_today.ymd_friendly(), basic_info);
    
    info!("{basic_info_str}");
    // println!("{basic_info_str}");
    basic_info
});


static MAP_YMD_BASICINFO: Lazy<SyncHashMap<i32, BasicFromChrono>> = Lazy::new(|| {
    SyncHashMap::new()
});

pub fn get_timestamp_date_0_oclock(date: &DateFT) -> (i64, TimeZone) {
    let BasicFromChrono {
        timezone,
        micros_offset,
        // date,
        // timestamp_date_0_oclock,
        ..
    } = BASIC_INFO_STARTUP.clone();

    let ymd = date.ymd_friendly();
    match MAP_YMD_BASICINFO.get(&ymd) {
        Some(v) => return (v.timestamp_date_0_oclock, timezone),
        None => (),
    };

    let str_today = format!("{}T00:00:00", date.to_print_10());

    let navie_today = chrono::NaiveDateTime::from_str(&str_today).expect("Unreachable");
    let timestamp_date_0_oclock = navie_today.timestamp_micros() - micros_offset;

    let basic_info = BasicFromChrono {
        timezone,
        micros_offset,
        date: date.clone(),
        timestamp_date_0_oclock,
    };
    let basic_info_str = basic_info.to_print();


    MAP_YMD_BASICINFO.insert(ymd, basic_info);

    info!("get_timestamp_date_0_oclock\n{basic_info_str}");
    println!("get_timestamp_date_0_oclock\n{basic_info_str}");

    (timestamp_date_0_oclock, timezone)

}


pub fn now() -> (SystemTime, DateTimeFT) {
    let system_time = SystemTime::now();
    let micros_since_unix_epoch: i64 = system_time.duration_since(SystemTime::UNIX_EPOCH).unwrap_or_else(|_e| Default::default()).as_micros() as i64;
    let timezone = BASIC_INFO_STARTUP.timezone;
    let (date, time) = to_date_time(micros_since_unix_epoch);

    (system_time, 
        DateTimeFT{ micros_since_unix_epoch, timezone, date, time })
}
pub fn now_onlytime() -> TimeFT {
    let micros_since_unix_epoch: i64 = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap_or_else(|_e| Default::default()).as_micros() as i64;
    // let timezone = BASIC_INFO_STARTUP.timezone;
    let (_, time) = to_date_time(micros_since_unix_epoch);

    time
}

pub(crate) fn to_date_time(micros_now: i64) -> (DateFT, TimeFT) {
    let BasicFromChrono {
        // timezone,
        // micros_offset,
        date,
        timestamp_date_0_oclock,
        ..
    } = BASIC_INFO_STARTUP.clone();

    let mut micros_today0 = timestamp_date_0_oclock;
    let mut micros_day = micros_now - micros_today0;
    let mut date_today = date;

    if micros_day < super::a8_micros::MICROS_PER_DAY {
        let time = TimeFT::from_secsday_micros(micros_day);

        return (date, time);
    }

    loop {
        date_today = date_today.tomorrow();
        micros_today0 += super::a8_micros::MICROS_PER_DAY;
        micros_day -= super::a8_micros::MICROS_PER_DAY;
        if micros_day < super::a8_micros::MICROS_PER_DAY {
            break;
        }
    }
    let time = TimeFT::from_secsday_micros(micros_day);

    (date_today, time)
}

pub fn from_std(time: SystemTime) -> DateTimeFT {
    let micros_since_unix_epoch: i64 = time.duration_since(SystemTime::UNIX_EPOCH).unwrap_or_else(|_e| Default::default()).as_micros() as i64;
    let timezone = BASIC_INFO_STARTUP.timezone;
    let (date, time) = to_date_time(micros_since_unix_epoch);

    DateTimeFT{ micros_since_unix_epoch, timezone, date, time }
}
pub fn from_micros(micros_since_unix_epoch: i64, timezone: Option<TimeZone>) -> DateTimeFT {
    let timezone = timezone.unwrap_or(BASIC_INFO_STARTUP.timezone);
    let (date, time) = to_date_time(micros_since_unix_epoch);

    DateTimeFT{ micros_since_unix_epoch, timezone, date, time }
}



pub(crate) fn to_date_time2(micros_now: i64, timezone_new: TimeZone) -> (DateFT, TimeFT) {
    let BasicFromChrono { timezone: _timezone, micros_offset, date, timestamp_date_0_oclock } = BASIC_INFO_STARTUP.clone();

    let micros_offset_new = timezone_new.micros_offset();
    
    let micros_today0 = timestamp_date_0_oclock + micros_offset - micros_offset_new;
    let mut micros_day = micros_now - micros_today0;
    let mut date_today = date;

    if micros_day < super::a8_micros::MICROS_PER_DAY {
        let time = TimeFT::from_secsday_micros(micros_day);

        return (date, time);
    }

    loop {
        date_today = date_today.tomorrow();
        // micros_today0 += super::a8_micros::MICROS_PER_DAY;
        micros_day -= super::a8_micros::MICROS_PER_DAY;
        if micros_day < super::a8_micros::MICROS_PER_DAY {
            break;
        }
    }
    let time = TimeFT::from_secsday_micros(micros_day);

    (date_today, time)
}

#[test]
fn test_to_date_time_20231022() {
    let now = DateTimeFT::now();
    let mut micros_since_unix_epoch = now.micros_since_unix_epoch;


    let timezone = BASIC_INFO_STARTUP.timezone;
    let (date, time) = to_date_time(micros_since_unix_epoch + super::a8_micros::MICROS_PER_DAY * 2);
    let dt = DateTimeFT {
        micros_since_unix_epoch,
        timezone,
        date,
        time,
    };
    println!("\t{}", dt.to_print());

    for _ in 0..100 {
        micros_since_unix_epoch += 3000_000_000_i64;
        let (date, time) = to_date_time(micros_since_unix_epoch);
        let dt = DateTimeFT {
            micros_since_unix_epoch,
            timezone,
            date,
            time,
        };
        println!("\t{}", dt.to_print());

    }
}


#[test]
fn test_from_micros() {
    // O":1727785067503,"C":1727871467503
    let dtO = from_micros(1728030231308, None);
    let time = dtO.time.to_print_hmsi();
    println!("{time}: {}", dtO.to_print_timezone());
    let dtC = from_micros(1728030291993, None);
    let time = dtC.time.to_print_hmsi();
    println!("{time}: {}", dtC.to_print_timezone());
}